<?php
/**
 * Install.php
 *
 * @category    Madoqua
 * @package     Install
 */

/**
 * Madoqua_Install
 *
 * Installer class
 *
 * @category    Madoqua
 * @package     Install
 */
class Madoqua_Install
{

    /**
     * View instance
     *
     * @var Zend_View_Interface
     */
    protected $_view = null;

    /**
     * Response objects
     *
     * @var Zend_Controller_Response_Http
     */
    protected $_response = null;

    /**
     * Host
     *
     * @var string
     */
    protected $_host = null;

    /**
     * Rewrite base
     *
     * @var string
     */
    protected $_rewriteBase = '/';

    /**
     * Path
     *
     * @var string
     */
    protected $_path = null;

    /**
     * Errors
     *
     * @var array
     */
    protected $_errors = array();

    /**
     * I can has installz?
     */
    public $canInstall = true;

    /**
     * directories that have to be writable
     *
     * @var array
     * @todo, just recursively scan data/ and make sure all subdirs are writeable
     */
    protected $_directories = array(
     	'applicationConfig' => array('madoqua_private/application/config', 0774),
       	'cache'             => array('madoqua_private/data/cache', 0774),
        'actionsCache'      => array('madoqua_private/data/cache/actions',0774),
        'configCache'       => array('madoqua_private/data/cache/config',0774),
        'newsCache'         => array('madoqua_private/data/cache/news',0774),
        'purifierCache'     => array('madoqua_private/data/cache/purifier',0774),
      	'sqliteDb'          => array('madoqua_private/data/database',0774),
      	'logs'              => array('madoqua_private/data/logs',0774),
        'openId'            => array('madoqua_private/data/openid',0774),
        'search'            => array('madoqua_private/data/search',0774),
        'sessions'          => array('madoqua_private/data/session',0774),
        'themes'            => array('madoqua_private/data/themes',0774)
    );

    /**
     * Constructor
     *
     * @return Madoqua_Install provides fluent interface
     */
    public function __construct ()
    {
        $this->_host = 'http://' . trim($_SERVER['HTTP_HOST'], '/');
        $this->_path = realpath(getcwd());

        //$this->_directories['rootPath'] = array($this->_path, 0775);
        //default host and path

        $this->_view = new Zend_View();
        $this->_view->setScriptPath($_SERVER['DOCUMENT_ROOT'] . 'madoqua_private/setup/views/scripts');

        $this->_view->addBasePath(
            $this->_path  . DIRECTORY_SEPARATOR .
            'madoqua_private/setup/views'
        );
        //set up view

        $this->_view->host = $this->_host;
        $this->_view->rewriteBase = $this->_rewriteBase;

        //set up view environment
        return $this;
    }

    /**
     * Set the response object
     *
     * @param Zend_Controller_Response_Http $response
     * @return Madoqua_Install provides fluent interface
     */
    public function setResponse(Zend_Controller_Response_Http $response = null)
    {
        if ($response === null) {
            $this->_response = new Zend_Controller_Response_Http();
        }  else {
            $this->_response = $response;
        }

        return $this;
    }

    /**
     * Return the response object
     *
     * @return Zend_Controller_Response_Http
     */
    public function getResponse()
    {
        if($this->_response === null) {
            $this->_response = new Zend_Controller_Response_Http();
        }

        return $this->_response;
    }

    /**
     * Set a view object for the isntaller
     *
     * @param Zend_View_Interface $view
     * @return return Madoqua_Installer provides fluent interface
     */
    public function setView(Zend_View_Interface $view)
    {
        $this->_view = $view;
        return $this;
    }

    /**
     * Get the installers view object
     *
     * @return Zend_View
     */
    public function getView()
    {
        if($this->_view === null) {
            $this->_view = new Zend_View();
        }
        return $this->_view;
    }

    /**
     * Set custom directory locations
     * @param array $dirs
     * @return Madoqua_Install provides fluent interface
     */
    public function setDirectories(array $dirs)
    {
        foreach ($dirs as $key => $dir) {
            if(!array_key_exists($key, $this->_directories)) {
                throw new Exception("Invalid directory key '$key'", Madoqua_Exception::WARNING );
            }
            $this->_directories[$key] = $dir;
        }
        return $this;
    }

    /**
     * Returns array of required dirs
     *
     * @return array
     */
    public function getDirectories()
    {
        return $this->_directories;
    }

    /**
     * Has .htaccess been installed and rewriting enabled?
     *
     * @return bool
     */
    protected function htaccessInstalled ()
    {
        if (file_exists($this->_path . DIRECTORY_SEPARATOR . '.htaccess')) {
            if (! empty($_SERVER['MADOQUAHOST']) || !empty($_SERVER['REDIRECT_MADOQUAHOST']) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Create directorys as required, then set permissions
     *
     * @TODO Something extendable and elegant with SPL to allow customisation.
     * @return void
     */
    private function setupDirectories ()
    {
        // Create required dirs if they dont already exist
        foreach ($this->_directories as $key => $checkDir) {
            if(!$key != 'rootpath') {
                $path = $this->_path . DIRECTORY_SEPARATOR .  $checkDir[0];
                // throw new Exception('no dir ' . $path );
                if (!file_exists($path)) {
                   chdir($this->_path);
                   mkdir('.' . DIRECTORY_SEPARATOR . $checkDir[0] , 0775, true);
                }
                if (!file_exists($path)) {
                    $this->_errors[] = "Directory $path could not be created, please create
    	            this directory so setup can continue.";
                }
            }
        }

        foreach ($this->_directories as $key => $dir ) {
            $pathName = $this->_path . DIRECTORY_SEPARATOR . $dir[0];
            $perms = 0774;

            if ($key != 'rootPath') {
                @chmod( $pathName, $perms);
            }

            if(!is_writable($pathName) && $key != 'rootPath') {

                $directory = new SplFileInfo($pathName);
                $currentPerms = substr(sprintf('%o', $directory->getPerms()), -4);
                $setPerms = ($currentPerms <= 0770) ? '0770' : '0777';

                $this->_errors[] = "Directory $pathName is not writable with permissions set
                to '$currentPerms' but needs to be made writable before setup can continue.
                Please set the permissions to $setPerms.";

                $this->canInstall = false;
            }
        }


    }

    /**
     * Write the default configuration then redirect to continue install
     *
     * @return $string Html markup if not redirected
     */
    protected function writeConfig ()
    {
        $configFile = $this->_path . DIRECTORY_SEPARATOR .
                      'madoqua_private/application/config/madoqua.xml';

        if (! file_exists($configFile)) {
            $sampleXml = file_get_contents(
                $this->_path . DIRECTORY_SEPARATOR .
                'madoqua_private/application/config/madoqua.sample.xml'
            );

            $replaceMatches = array(
                '{baseurl}' ,
                '{url}'
            );

            if(isset($_SERVER['MADOQUAHOST']) && isset($_SERVER['REWRITEBASE'])) {
                $host = $_SERVER['MADOQUAHOST'];
                $rwBase = $_SERVER['REWRITEBASE'];
            } elseif(isset($_SERVER['REDIRECT_MADOQUAHOST']) && isset($_SERVER['REDIRECT_REWRITEBASE'])) {
                $host = $_SERVER['REDIRECT_MADOQUAHOST'];
                $rwBase = $_SERVER['REDIRECT_REWRITEBASE'];
            } else {
                throw new Exception('Error writing config file, unable to determine environment...');
            }

            $replaceValues = array(
                $rwBase ,
                $host
            );
            $sampleXml = str_replace($replaceMatches, $replaceValues, $sampleXml);

            if (! file_put_contents($configFile, $sampleXml)) {
                throw new Madoqua_Exception("Could not write to the config file: $configFile", 0);
            }
        }

        if (isset($_GET['redirect'])) {
            //we have been redirected, continue with normal setup
            $locationParts = array(
                $_SERVER['MADOQUAHOST'] ,
                trim($_SERVER['REWRITEBASE'], '/') ,
                'install'
            );
            //user can go to "proper" installation
            return $this->_redirect($locationParts);
        }

        //user tried to restart setup
        return $this->_render('installed');
    }

    protected function startSetup ()
    {
        // Bail if we can't install
        if (! $this->canInstall) {
            $this->_view->errors = $this->_errors;
            return $this->_render('permissions');
        }

        // .htaccess location
        $file = $this->_path . DIRECTORY_SEPARATOR.  '.htaccess';

        // get the rewrite base (replace \ with / for windows hosts)
        $this->_rewriteBase = str_replace('\\', '/', dirname($_SERVER['REQUEST_URI']));

        if (file_exists($file)) {
            // Already setup .htaccess, bail
            return $this->_render('htaccess');

        } elseif (! empty($_POST['track'])) {
            // grab default .htaccess data
            $this->_rewriteBase = '/' . trim(@$_POST['rewritebase'], '/');
            $this->_host = trim(@$_POST['host']);
            $htaccess = file_get_contents($this->_path . DIRECTORY_SEPARATOR . 'madoqua_private/setup/.htaccess.default');

            // merge in server data
            $htaccess = str_replace('{rewritebase}', $this->_rewriteBase, $htaccess);
            $htaccess = str_replace('{host}', $this->_host, $htaccess);

            if (! file_put_contents($file, $htaccess)) {
                //we can't write, noes!
                $errors[] = 'Could not write to file: ' . $file;
                $errors[] = 'Please save the following .htaccess directives to your web/vhost root.';

                // Assign htaccess to view so they can manually write it
                // TODO send this to client browser as filestream
                $this->_view->htaccess = $htaccess;
                return $this->_render('problem');

            } else {

                //we can write, woo!
                $locationParts = array(
                    $this->_host ,
                    $this->_rewriteBase ,
                    '/setup.php?redirect=1'
                );

                //redirect user back to check if installation worked
                return $this->_redirect($locationParts);
            }
        }

        //assign errors to view, then start proper setup
        $this->_view->errors = $this->_errors;
        return $this->_render('setup');
    }

    /**
     * Execute the install
     *
     * @return Zend_Controller_Response_Http
     */
    public function execute ()
    {
        $this->setupDirectories();

        if ($this->htaccessInstalled()) {
            // We already installed an .htaccess, write the config or bail
            try {
                $this->writeConfig();
            } catch (Exception $e) {
                $this->_view->errors = $this->_errors;
                return $this->_render('permissions');
            }
            if(!$this->canInstall) {
                $this->_view->errors = $this->_errors;
                return $this->_render('permissions');
            }
            $this->_redirect('setup.php?redirect=1');

        } elseif (isset($_GET['redirect'])) {
            //we did try an install but it failed
            return $this->_render('problem');

        } else {
           // Try to write an .htaccess to start the setup
           return $this->startSetup();
        }
    }

    /**
     * Render the given view script
     *
     * @param string $script
     * @return Zend_Controller_Response_Http
     */
    protected function _render ($script)
    {
        $script = $script . '.phtml';
        //find the view script
        $xhtml = $this->_view->render('header.phtml');
        //render header
        $xhtml .= $this->_view->render($script);
        //render script itself
        $xhtml .= $this->_view->render('footer.phtml');
        //render footer

        $this->getResponse()->setBody($xhtml);
        return $this->_response;
    }

    /**
     * Redirect to the give location
     * If an array is given, it gets imploded with a forward slash.
     *
     * @param string|array $location
     * @return Zend_Controller_Response_Http or exit
     */
    protected function _redirect ($location)
    {
        if (is_array($location)) {
            $location = array_filter($location); //remove empty array elements
            $location = implode('/', $location);
        }

        // Set the redirect & code, send it if we can
        $this->getResponse()->setHttpResponseCode(307);
        $this->getResponse()->setHeader('Location', $location);

        if($this->_response->canSendHeaders()) {
            $this->_response->sendHeaders();
            exit();
        }

        return $this->_response;
    }
}